/*
 * File:   GameState.h
 * Author: root
 *
 * Created on November 13, 2008, 7:39 PM
 */

#include "GameState.h"
#include "Query.h"

GameState::GameState() :
  round(DEAL_ROUND),
  currPlayer(0),
  currPhase(NoGame),
  knocked(false),
  knockPlayer(-1)
{
  deck.NewDeck();
  deck.SetCardsFaceUp(false);
  deck.Shuffle();

  for (int i = 0; i < NUM_CARDS; i++) {
    cardLocation[i].stack = Card::InDeck;
    cardLocation[i].player = -1;
    isKnown[i] = false;
    for (int j=0; j < NUM_PLAYERS; j++) {
      cardHistory[j][i] = Card::NotTouched;
    }
  }

// Important: start the discard pile!
  CCard firstDiscard = deck.Pop();
  firstDiscard.SetFaceUp(true);
  Query::debugOut(string("The first discard is ") + firstDiscard.Initials() + ". \n", 1);
  discard.Push(firstDiscard);
  int index = firstDiscard.GetIndex() - 1;
  cardLocation[index].stack = Card::InDiscard;
  isKnown[index] = true;

  for (int i = 0; i < NUM_PLAYERS; i++) {
    lastPickUpRound[i] = DEAL_ROUND;
  }

  // Strategy-specific objects init
  for (int i = 0; i < NUM_PLAYERS; i++) {
      for (int j = Card::Spades; j <= Card::Diamonds; j++) {
          // OppSuitTrackDiscardStrategy init
          suitHistory[i][j] = 0;
          // Point bracketing init
          positiveSuitHistory[i][j] = 0;
      }
      // Point bracketing strategies init
      minPoints[i] = 2;
      scoringSuit[i] = -1;
  }

}

CCardStack GameState::deal(int player) {
  if (round==DEAL_ROUND && currPlayer==player) {
    CCardStack hand = deck.Pop(3);

    Query::debugOut(string("Dealt ") + string(hand.Initials()) + string(" to "), 2);
    Query::debugValue(player, 2);

    for (VI hi = hand.begin(); hi != hand.end(); hi++) {
      int index = hi->GetIndex() - 1;
      cardLocation[index].stack = Card::InPlayerHand;
      cardLocation[index].player = player;
      cardHistory[player][index] |= Card::Drawn;
    }

    advancePlayer();
    if (round > DEAL_ROUND)
      currPhase = DrawPhase;

    return hand;
  }
  else {
    Query::debugOut("Tried to be dealt a  out of turn: ", 1);
    Query::debugValue(player, 1);
    return CCardStack();
  }
}

void GameState::knock(int player) {
  if (currPhase==DrawPhase && currPlayer==player) {
    knocked = true;
    knockPlayer = player;
    visibleMoveHistory[player].push_back(
          Move(true,INVALID_CARD,INVALID_CARD,INVALID_CARD));
    advancePlayer();
  }
  else {
    Query::debugOut("Tried to knock out of turn: ", 1);
    Query::debugValue(player, 1);
  }
}


/**
 *Draw from the face-down pile
 *If the last card is taken, begin last round
 *If the pile is empty, take from discard instead
 */
CCard GameState::draw(int player) {
  if (deck.Empty()) {
    Query::debugOut(string("Tried to draw, but the deck was empty.\n") +
                           "\tGiving him the top discard instead: ", 1);
    Query::debugValue(player, 1);
    return pickUp(player);
  }
  if (currPlayer==player && currPhase==DrawPhase) {
    CCard drawnCard = deck.Pop();

    Query::debugOut(string("Drew ") + string(drawnCard.Initials()) + string(": "), 2);
    Query::debugValue(player, 2);
    Query::debugOut("Cards remaining: ", 2);
    Query::debugValue(deck.Size(), 2);

    int index = drawnCard.GetIndex() - 1;
    cardLocation[index].stack = Card::InPlayerHand;
    cardLocation[index].player = player;
    cardHistory[player][index] |= Card::Drawn;
    // CardDrawn is never visible in this move history
    visibleMoveHistory[player].push_back(
            Move(false,INVALID_CARD,INVALID_CARD,INVALID_CARD));

    if (deck.Empty()) {
      Query::debugOut(string("Took the last draw card: "), 1);
      Query::debugValue(player, 1);

      knocked = true;
      knockPlayer = player;
    }
    currPhase = DiscardPhase;
    return drawnCard;
  }
  else {
    Query::debugOut("Tried to draw out of turn: ", 1);
    Query::debugValue(player, 1);

    return CCard(INVALID_CARD);
  }
}

/**
 *Pick the top card from the discard pile
 */
CCard GameState::pickUp(int player) {
  if (discard.Empty()) {
    Query::debugOut("Why is the discard pile empty?!\n", 1);
    return CCard(INVALID_CARD);
  }
  if (currPlayer==player && currPhase==DrawPhase && !discard.Empty()) {
    CCard pickedCard = discard.Pop();

    Query::debugOut(string("Picked up ") + string(pickedCard.Initials()) +
                    string(": "), 2);
    Query::debugValue(player, 2);
    int index = pickedCard.GetIndex() - 1;
    cardLocation[index].stack = Card::InPlayerHand;
    cardLocation[index].player = player;
    cardHistory[player][index] |= Card::PickedUp;
    if (cardHistory[player][index] & Card::Discarded) {
      Query::debugOut(string("Card picked up before by: "), 2);
      Query::debugValue(player, 2);
      Query::debugOut(string("Resetting discard status.\n"), 2);
      cardHistory[player][index] &= ~Card::Discarded;
    }
    visibleMoveHistory[player].push_back(
            Move(false,INVALID_CARD,pickedCard,INVALID_CARD));
    lastPickUpRound[player] = round;
    currPhase = DiscardPhase;

    // Strategy-specific state update
    int cardSuit = pickedCard.Suit();
    int cardValue = pickedCard.Value();
    // OppSuitTrackDiscardStrategy state
    suitHistory[player][cardSuit] += cardValue;
    // Point bracketing strategies state
    positiveSuitHistory[player][cardSuit] += cardValue;
    int bestSuit = Query::maxValueIndex(positiveSuitHistory[player], 4);
    if (suitHistory[player][bestSuit] > minPoints[player]) {
        minPoints[player] = suitHistory[player][bestSuit];
        scoringSuit[player] = bestSuit;
    }
    if (scoringSuit[player] != cardSuit && scoringSuit[player] != -1) {
        if (suitHistory[player][cardSuit] == suitHistory[player][scoringSuit[player]]) {
            scoringSuit[player] = cardSuit;
        }
    }


//    if (lastPickedSuit[player] == cardSuit) {
//        minPoints[player] += cardValue;
//    }
//    else {
//        lastPickedSuit[player] = cardSuit;
//        if (cardValue > minPoints[player]) {
//            minPoints[player] = cardValue;
//        }
//    }


    return pickedCard;
  }
  else {
    Query::debugOut(string("Tried to pick up out of turn: "), 1);
    Query::debugValue(player, 1);
    return CCard(INVALID_CARD);
  }
}

/**
 *Called when a player discards a card. If the card is a known card (previously picked from the discard),
 *it will be deleted from the player's known hand
 */
void GameState::doDiscard(CCard discarded, int player) {
  int index = discarded.GetIndex() - 1;

  Query::debugOut(string("Discarded ") + string(discarded.Initials()) + ": ", 2);
  Query::debugValue(player, 2);

  if (currPlayer==player && currPhase==DiscardPhase) {
    if (cardLocation[index].stack==Card::InPlayerHand &&
        cardLocation[index].player==player) {
      cardLocation[index].stack = Card::InDiscard;
      cardLocation[index].player = -1;
      isKnown[index] = true;
      cardHistory[player][index] |= Card::Discarded;
    // Last move in history should be current, due to draw/discard
      visibleMoveHistory[player].back().setDiscarded(index);
      discarded.SetFaceUp(true);
      discard.Push(discarded);

      // Strategy-specific state update
      int cardSuit = discarded.Suit();
      int cardValue = discarded.Value();
      // OppSuitTrackDiscardStrategy state
      suitHistory[player][cardSuit] -= cardValue;
      // Point bracketing strategies
      if (cardValue > minPoints[player]) {
          minPoints[player] = cardValue;
      }
      int aux = positiveSuitHistory[player][cardSuit] - cardValue;
      positiveSuitHistory[player][cardSuit] = (aux > 0) ? aux : 0;
      int bestSuit = Query::maxValueIndex(positiveSuitHistory[player], 4);
      if (positiveSuitHistory[player][bestSuit] > 0) {
          scoringSuit[player] = bestSuit;
      }

//       if (
      advancePlayer();
      if (isLastRound() && knockPlayer==currPlayer) {
        currPhase = GameOver;
      }
      else {
        currPhase = DrawPhase;
      }
    }
    else {
      Query::debugOut("Tried to discard a card that isn't in his hand: ", 1);
      Query::debugValue(player, 1);
    }
  }
  else {
    Query::debugOut("Tried to discard out of turn: ", 1);
    Query::debugValue(player, 1);
  }
}

void GameState::report31(int player) {
  currPhase = GameOver;
}

/**
 * A list of all cards ever discarded by player
 * Returns a sorted list
 */
CCardStack GameState::getCardsDiscardedByPlayer(int player) const {
  CCardStack cards;
  for (int i=0; i<NUM_CARDS; i++) {
    if (cardHistory[player][i] & Card::Discarded)
      cards.Push(CCard(i+1));
  }
  return cards;
}

/**
 * A list of all cards ever picked up from the discard pile by player
 * Returns a sorted list
 */
CCardStack GameState::getCardsPickedUpByPlayer(int player) const {
  CCardStack cards;
  for (int i=0; i<NUM_CARDS; i++) {
    if (cardHistory[player][i] & Card::PickedUp)
      cards.Push(CCard(i+1));
  }
  return cards;
}

/**
 * A list of all cards picked up from the discard pile by player
 * and not discarded (known cards in player's hand)
 * Returns a sorted list
 */
CCardStack GameState::getCardsPickedUpAndHeldByPlayer(int player) const {
  CCardStack cards;
  for (int i=0; i<NUM_CARDS; i++) {
    int state = cardHistory[player][i];
    if ((state & Card::PickedUp) && !(state & Card::Discarded))
      cards.Push(CCard(i+1));
  }
  return cards;
}

/**
 * A list of all cards that have been shown face-up
 * Essentially, every card that's been discarded
 * (which is different from the current contents of the discard pile)
 * Returns a sorted list
 */
CCardStack GameState::getCardsShown() const {
  CCardStack cards;
  for (int i=0; i<NUM_CARDS; i++) {
    bool select=false;
    for (int j=0; j<NUM_PLAYERS && !select; j++) {
      int state = cardHistory[j][i];
      if (state & Card::Discarded) {
        select=true;
        cards.Push(CCard(i+1));
      }
    }
  }
  return cards;
}

/**
 * A list of all cards that have never been shown face-up
 * Essentially, every card that hasn't been discarded
 * Returns a sorted list
 */

CCardStack GameState::getCardsNotShown() const {
  CCardStack cards;
  for (int i=0; i<NUM_CARDS; i++) {
    bool select=true;
    for (int j=0; j<NUM_PLAYERS && select; j++) {
      int state = cardHistory[j][i];
      if (state & Card::Discarded)
        select=false;
    }
    if (select)
      cards.Push(CCard(i+1));
  }
  return cards;
}

CCardStack GameState::getUnknownCards(int playerPerspective) const {
    CCardStack cards;
    for (int i = 0; i < NUM_CARDS; i++) {
        if (cardLocation[i].stack == Card::InDeck
            || (!isKnown[i]
                && cardLocation[i].stack == Card::InPlayerHand
                && cardLocation[i].player != playerPerspective))
        {
            cards.Push(CCard(i+1));
        }
    }
    return cards;
}

const MoveHistory* GameState::getPlayerMoveHistory(int player) const {
  return visibleMoveHistory + player;
}

Move GameState::getPlayersLastMove(int player) const {
    Move ret;
    if (visibleMoveHistory[player].size() > 0) {
        ret = visibleMoveHistory[player].back();
    }
    return ret;
}

Move GameState::getPlayersLastPickUpMove(int player) const {
  int targetRound = lastPickUpRound[player];
  if (targetRound != DEAL_ROUND) {
    return visibleMoveHistory[player][targetRound];
  }
  else {
    return Move(false,INVALID_CARD,INVALID_CARD,INVALID_CARD);
  }
}


void GameState::advancePlayer() {
  currPlayer++;
  if (currPlayer == NUM_PLAYERS) {
    currPlayer = 0;
    round++;
  }
}

float GameState::singleDrawProbability(int playerPerspective) const {
    int numUnknown = 0;
    for (int i = 0; i < NUM_CARDS; i++) {
        if (cardLocation[i].stack == Card::InDeck
            || (!isKnown[i]
                && cardLocation[i].stack == Card::InPlayerHand
                && cardLocation[i].player != playerPerspective))
        {
            numUnknown++;
        }
    }
    return (1.0f / (numUnknown));
}

//int GameState::powerOfCard(int playerPerspective, const CCard& card) const {
//    int numUnknown = 0;
//    int numLessThan = 0;
//    for (int i = 0; i < NUM_CARDS; i++) {
//        if (cardLocation[i].stack == Card::InDeck
//            || (!isKnown[i]
//                && cardLocation[i].stack == Card::InPlayerHand
//                && cardLocation[i].player != playerPerspective))
//        {
//            numUnknown++;
//            CCard possibleCard(i+1);
//            if (possibleCard.Value())
//        }
//    }
//}
